package com.springBoot.springBootSysCore.modules.controller.web.base;

import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.*;
import com.springBoot.springBootSysCore.common.utils.DateUtils;
import com.springBoot.springBootSysCore.common.utils.encoding.Encodes;
import com.springBoot.springBootSysCore.common.utils.json.JsonUtil;
import com.springBoot.springBootSysCore.common.utils.returnUtil.CodeEnum;
import com.springBoot.springBootSysCore.common.utils.returnUtil.ResultJsonFormat;
import org.apache.commons.collections.map.HashedMap;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.sql.Date;
import java.util.*;

/**
 * Created by timo on 2018/1/4.
 */
@Controller
@RequestMapping("oss")
public class OssController {

    @Value("${oss.endpoint}")
    String endpoint ;
    @Value("${oss.accessKeyId}")
    String accessId ;
    @Value("${oss.accessKeySecret}")
    String accessKey ;
    @Value("${oss.bucketName}")
    String bucket ;
    @Value("${oss.upfile_defaltdir_us}")
    String targetKey;

    /**
     * oss鉴权
     * @param request
     * @param response
     * @return
     */
    @RequestMapping("postObjectPolicy")
    @ResponseBody
    public Object postObjectPolicy(HttpServletRequest request, HttpServletResponse response){
        String dir = "user-dir";
        if (null != request.getParameter("dir")) {
            dir = request.getParameter("dir");
        }
        String host = "http://" + bucket + "." + endpoint;

        String endpoints = "http://" + endpoint;
        com.aliyun.oss.OSSClient client = new com.aliyun.oss.OSSClient(endpoints, accessId, accessKey);
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = client.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = client.calculatePostSignature(postPolicy);

            Map<String, String> respMap = new LinkedHashMap<String, String>();
            respMap.put("accessid", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            //respMap.put("expire", formatISO8601Date(expiration));
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            Map celMap = new HashedMap();
            celMap.put("callbackUrl",request.getParameter("callback"));
            celMap.put("callbackBodyType","application/json");
            celMap.put("callbackBody","bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&my_var=${x:var}");

            respMap.put("callback", Encodes.encodeBase64(JsonUtil.objToJson(celMap).toString().getBytes()));
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "GET, POST");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setStatus(HttpServletResponse.SC_OK);

            client.shutdown();
            return JsonUtil.objToJson(respMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * oss回调并返回图片访问路径
     * @param request
     * @return
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping("callback")
    @ResponseBody
    public Object celBack(HttpServletRequest request)throws ServletException, IOException {
        String ossCallbackBody = GetPostBody(request.getInputStream(), Integer.parseInt(request.getHeader("content-length")));
        boolean ret = VerifyOSSCallbackRequest(request, ossCallbackBody);
        String endpoints = "http://" + endpoint;
        com.aliyun.oss.OSSClient client = new com.aliyun.oss.OSSClient(endpoints, accessId, accessKey);

        String[] strs = ossCallbackBody.split("&");
        Map<String, String> m = new HashMap<String, String>();
        for(String s:strs){
            String[] ms = s.split("=");
            m.put(ms[0], ms[1].substring(1,ms[1].length()-1));
        }
        int fileSize = Integer.valueOf(m.get("size"));
        String sourceBucketName = m.get("bucket").toString();
        String sourceKey=m.get("my_var");
        String targetBucketName = sourceBucketName;
        if(fileSize>1024*1024*1024){ //大于1G
            // 得到被拷贝object大小
            ObjectMetadata objectMetadata = client.getObjectMetadata(sourceBucketName, sourceKey);
            long contentLength = objectMetadata.getContentLength();
            // 分片大小，10MB
            long partSize = 1024 * 1024 * 10;
            // 计算分块数目
            int partCount = (int) (contentLength / partSize);
            if (contentLength % partSize != 0) {
                partCount++;
            }
            System.out.println("total part count:" + partCount);
            // 初始化拷贝任务
            InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(targetBucketName, targetKey);
            InitiateMultipartUploadResult initiateMultipartUploadResult = client.initiateMultipartUpload(initiateMultipartUploadRequest);
            String uploadId = initiateMultipartUploadResult.getUploadId();
            // 分片拷贝
            List<PartETag> partETags = new ArrayList<PartETag>();
            for (int i = 0; i < partCount; i++) {
                // 计算每个分块的大小
                long skipBytes = partSize * i;
                long size = partSize < contentLength - skipBytes ? partSize : contentLength - skipBytes;
                // 创建UploadPartCopyRequest
                UploadPartCopyRequest uploadPartCopyRequest =
                        new UploadPartCopyRequest(sourceBucketName, sourceKey, targetBucketName, targetKey);
                uploadPartCopyRequest.setUploadId(uploadId);
                uploadPartCopyRequest.setPartSize(size);
                uploadPartCopyRequest.setBeginIndex(skipBytes);
                uploadPartCopyRequest.setPartNumber(i + 1);
                UploadPartCopyResult uploadPartCopyResult = client.uploadPartCopy(uploadPartCopyRequest);
                // 将返回的PartETag保存到List中
                partETags.add(uploadPartCopyResult.getPartETag());
            }
            // 提交分片拷贝任务
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(targetBucketName, targetKey, uploadId, partETags);
            client.completeMultipartUpload(completeMultipartUploadRequest);
        }else {
            // 拷贝Object
            CopyObjectResult result = client.copyObject(sourceBucketName, sourceKey, targetBucketName, targetKey);
            System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
        }

        URL url = client.generatePresignedUrl(bucket,targetKey, DateUtils.parseDate(DateUtils.getDateAfterWhatMS(new java.util.Date(),36500)));
        ResultJsonFormat rj = new  ResultJsonFormat(url,CodeEnum.SUCCESS.value());
        client.shutdown();
        return rj.convertResultJsonForData();
    }

    public String getParamString(Map<String, String[]> map) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String[]> e : map.entrySet()) {
            sb.append(e.getKey()).append("=");
            String[] value = e.getValue();
            if (value != null && value.length == 1) {
                sb.append(value[0]).append("\t");
            } else {
                sb.append(Arrays.toString(value)).append("\t");
            }
        }
        return sb.toString();
    }


    public static boolean doCheck(String content, byte[] sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            boolean bverify = signature.verify(sign);
            return bverify;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    @SuppressWarnings({"finally"})
    public String executeGet(String url) {
        BufferedReader in = null;

        String content = null;
        try {
            // 定义HttpClient
            @SuppressWarnings("resource")
            DefaultHttpClient client = new DefaultHttpClient();
            // 实例化HTTP方法
            HttpGet request = new HttpGet();
            request.setURI(new URI(url));
            HttpResponse response = client.execute(request);

            in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                sb.append(line + NL);
            }
            in.close();
            content = sb.toString();
        } catch (Exception e) {
        } finally {
            if (in != null) {
                try {
                    in.close();// 最后要关闭BufferedReader
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return content;
        }
    }

    public String GetPostBody(InputStream is, int contentLen) {
        if (contentLen > 0) {
            int readLen = 0;
            int readLengthThisTime = 0;
            byte[] message = new byte[contentLen];
            try {
                while (readLen != contentLen) {
                    readLengthThisTime = is.read(message, readLen, contentLen - readLen);
                    if (readLengthThisTime == -1) {// Should not happen.
                        break;
                    }
                    readLen += readLengthThisTime;
                }
                return new String(message);
            } catch (IOException e) {
            }
        }
        return "";
    }

    public boolean VerifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody) throws NumberFormatException, IOException {
        boolean ret = false;
        String autorizationInput = new String(request.getHeader("Authorization"));
        String pubKeyInput = request.getHeader("x-oss-pub-key-url");
        byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
        byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
        String pubKeyAddr = new String(pubKey);
        if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/") && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) {
            System.out.println("pub key addr must be oss addrss");
            return false;
        }
        String retString = executeGet(pubKeyAddr);
        retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
        retString = retString.replace("-----END PUBLIC KEY-----", "");
        String queryString = request.getQueryString();
        String uri = request.getRequestURI();
        String decodeUri = java.net.URLDecoder.decode(uri, "UTF-8");
        String authStr = decodeUri;
        if (queryString != null && !queryString.equals("")) {
            authStr += "?" + queryString;
        }
        authStr += "\n" + ossCallbackBody;
        ret = doCheck(authStr, authorization, retString);
        return ret;
    }
}
